summaryrefslogtreecommitdiff
path: root/app/api/swp/download/[fileId]/route.ts
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-10-27 17:14:44 +0900
committerjoonhoekim <26rote@gmail.com>2025-10-27 17:14:44 +0900
commit02062af723f1a3c2994c3e80148da47b07712713 (patch)
treefa9e27d9b0d9a9f87ef7ccf05edfb9c2806c06bd /app/api/swp/download/[fileId]/route.ts
parent2e92d5f83ae5f0f39090552b46c519982e9279c9 (diff)
(김준회) SWP 다운로드, 업로드 api route 처리, 옥프로 컬럼 순서 조정 처리, 환경변수 오타 수정
Diffstat (limited to 'app/api/swp/download/[fileId]/route.ts')
-rw-r--r--app/api/swp/download/[fileId]/route.ts172
1 files changed, 172 insertions, 0 deletions
diff --git a/app/api/swp/download/[fileId]/route.ts b/app/api/swp/download/[fileId]/route.ts
new file mode 100644
index 00000000..3af560aa
--- /dev/null
+++ b/app/api/swp/download/[fileId]/route.ts
@@ -0,0 +1,172 @@
+import { NextRequest, NextResponse } from "next/server";
+import * as fs from "fs/promises";
+import * as path from "path";
+import { eq } from "drizzle-orm";
+import db from "@/db/db";
+import { swpDocumentFiles } from "@/db/schema/SWP/swp-documents";
+import { debugLog, debugError, debugSuccess } from "@/lib/debug-utils";
+
+// API Route 설정
+export const runtime = "nodejs";
+export const maxDuration = 60; // 1분 타임아웃
+
+/**
+ * GET /api/swp/download/[fileId]
+ * 파일 다운로드 (바이너리 직접 전송)
+ */
+export async function GET(
+ request: NextRequest,
+ { params }: { params: Promise<{ fileId: string }> }
+) {
+ try {
+ const { fileId: fileIdString } = await params;
+ const fileId = parseInt(fileIdString, 10);
+
+ if (isNaN(fileId)) {
+ return NextResponse.json(
+ { success: false, error: "잘못된 파일 ID입니다." },
+ { status: 400 }
+ );
+ }
+
+ debugLog(`[download] 다운로드 시작`, { fileId });
+
+ // 1. 파일 정보 조회
+ const fileInfo = await db
+ .select({
+ FILE_NM: swpDocumentFiles.FILE_NM,
+ FLD_PATH: swpDocumentFiles.FLD_PATH,
+ })
+ .from(swpDocumentFiles)
+ .where(eq(swpDocumentFiles.id, fileId))
+ .limit(1);
+
+ if (!fileInfo || fileInfo.length === 0) {
+ debugError(`[download] 파일 정보 없음`, { fileId });
+ return NextResponse.json(
+ { success: false, error: "파일 정보를 찾을 수 없습니다." },
+ { status: 404 }
+ );
+ }
+
+ const { FILE_NM, FLD_PATH } = fileInfo[0];
+ debugLog(`[download] 파일 정보 조회 완료`, { FILE_NM, FLD_PATH });
+
+ if (!FLD_PATH || !FILE_NM) {
+ debugError(`[download] 파일 경로 또는 이름 없음`, { FILE_NM, FLD_PATH });
+ return NextResponse.json(
+ { success: false, error: "파일 경로 또는 파일명이 없습니다." },
+ { status: 404 }
+ );
+ }
+
+ // 2. NFS 마운트 경로 확인
+ const nfsBasePath = process.env.SWP_MOUNT_DIR;
+ if (!nfsBasePath) {
+ debugError(`[download] SWP_MOUNT_DIR 환경변수가 설정되지 않았습니다.`);
+ return NextResponse.json(
+ { success: false, error: "서버 설정 오류: NFS 경로가 설정되지 않았습니다." },
+ { status: 500 }
+ );
+ }
+
+ // 3. 전체 파일 경로 생성
+ const normalizedFldPath = FLD_PATH.replace(/\\/g, '/');
+ const fullPath = path.join(nfsBasePath, normalizedFldPath, FILE_NM);
+
+ debugLog(`[download] 파일 경로`, {
+ fileId,
+ FILE_NM,
+ FLD_PATH,
+ normalizedFldPath,
+ fullPath,
+ });
+
+ // 4. 파일 존재 여부 확인
+ try {
+ await fs.access(fullPath, fs.constants.R_OK);
+ } catch (accessError) {
+ debugError(`[download] 파일 접근 불가`, { fullPath, error: accessError });
+ return NextResponse.json(
+ { success: false, error: `파일을 찾을 수 없습니다: ${FILE_NM}` },
+ { status: 404 }
+ );
+ }
+
+ // 5. 파일 읽기
+ debugLog(`[download] 파일 읽기 시작`, { fullPath });
+ const fileBuffer = await fs.readFile(fullPath);
+
+ debugLog(`[download] 파일 Buffer 읽기 완료`, {
+ bufferLength: fileBuffer.length,
+ isBuffer: Buffer.isBuffer(fileBuffer),
+ bufferType: typeof fileBuffer,
+ constructor: fileBuffer.constructor.name,
+ first20Bytes: fileBuffer.slice(0, 20).toString('hex')
+ });
+
+ // 6. MIME 타입 결정
+ const mimeType = getMimeType(FILE_NM);
+
+ debugSuccess(`[download] 다운로드 성공`, {
+ fileName: FILE_NM,
+ size: fileBuffer.length,
+ mimeType,
+ });
+
+ // 7. 바이너리 응답 반환
+ return new NextResponse(fileBuffer, {
+ status: 200,
+ headers: {
+ "Content-Type": mimeType,
+ "Content-Disposition": `attachment; filename="${encodeURIComponent(FILE_NM)}"`,
+ "Content-Length": String(fileBuffer.length),
+ },
+ });
+ } catch (error) {
+ console.error("[download] 오류:", error);
+ debugError(`[download] 다운로드 실패`, {
+ error: error instanceof Error ? error.message : String(error),
+ stack: error instanceof Error ? error.stack : undefined
+ });
+
+ return NextResponse.json(
+ {
+ success: false,
+ error: error instanceof Error ? error.message : "파일 다운로드 실패",
+ },
+ { status: 500 }
+ );
+ }
+}
+
+/**
+ * MIME 타입 결정
+ */
+function getMimeType(fileName: string): string {
+ const ext = path.extname(fileName).toLowerCase();
+
+ const mimeTypes: Record<string, string> = {
+ ".pdf": "application/pdf",
+ ".doc": "application/msword",
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ ".xls": "application/vnd.ms-excel",
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ ".ppt": "application/vnd.ms-powerpoint",
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ ".txt": "text/plain",
+ ".csv": "text/csv",
+ ".jpg": "image/jpeg",
+ ".jpeg": "image/jpeg",
+ ".png": "image/png",
+ ".gif": "image/gif",
+ ".zip": "application/zip",
+ ".rar": "application/x-rar-compressed",
+ ".7z": "application/x-7z-compressed",
+ ".dwg": "application/acad",
+ ".dxf": "application/dxf",
+ };
+
+ return mimeTypes[ext] || "application/octet-stream";
+}
+